/*
 * iic.c
 *
 *  Created on: Sep 27, 2020
 *      Author: 16330
 */

#include "iic.h"

static void delay(unsigned char us) //大概延时
{
	uint8_t i = 10;
	while(us--)
	{
	    while(i--);
	}
}

void I2C_Stop(struct IIC_COMMON *this) //产生起始信号
{
	this->sda->WritePin(this->sda,GPIO_PIN_RESET);	//信号线置低
	delay(1);
	this->scl->WritePin(this->scl,GPIO_PIN_SET);	//时钟线置高
	delay(1);
	this->sda->WritePin(this->sda,GPIO_PIN_SET);	//信号线置高
	delay(1);
}

void I2C_Start(struct IIC_COMMON *this) //产生停止信号
{
	this->scl->WritePin(this->scl,GPIO_PIN_SET);	//时钟线置高
	this->sda->WritePin(this->sda,GPIO_PIN_SET);	//信号线置高
	delay(1);
	this->sda->WritePin(this->sda,GPIO_PIN_RESET);	//信号线置低
	delay(1);
	this->scl->WritePin(this->scl,GPIO_PIN_RESET);	//时钟线置低
	delay(1);
}

static unsigned char IIC_Wait_Ack(struct IIC_COMMON *this) //阻塞等待从机应答
{
	unsigned char ack=0;

	this->scl->WritePin(this->scl,GPIO_PIN_RESET);	//时钟线置低
	delay(1);
	this->sda->WritePin(this->sda,GPIO_PIN_SET);	//信号线置高
	delay(1);
	this->scl->WritePin(this->scl,GPIO_PIN_SET);	//时钟线置高
	delay(1);

	if(this->sda->ReadPin(this->sda)){	//读取SDA的电平
	    ack = IIC_NO_ACK;	//如果为1，则从机没有应答
	}
	else{
	    ack = IIC_ACK; //如果为0，则从机应答
	}
	this->scl->WritePin(this->scl,GPIO_PIN_RESET);//时钟线置低
	delay(1);

	return ack;	//返回读取到的应答信息
}

void I2C_Send_Byte(struct IIC_COMMON *this,uint8_t IIC_Byte)
{
	unsigned char i;  //定义变量
	for(i=0;i<8;i++) //for循环8次
	{
	    this->scl->WritePin(this->scl,GPIO_PIN_RESET);	//时钟线置低，为传输数据做准备
	    delay(1);
	    if(IIC_Byte & 0x80)	//读取最高位
	      	this->sda->WritePin(this->sda,GPIO_PIN_SET);
	    else
	  	    this->sda->WritePin(this->sda,GPIO_PIN_RESET);
	    IIC_Byte <<= 1;  //数据左移1位
	    delay(1);
	    this->scl->WritePin(this->scl,GPIO_PIN_SET); //时钟线置高，产生上升沿，把数据发送出去
	    delay(1);
	}
	this->scl->WritePin(this->scl,GPIO_PIN_RESET);	//时钟线置低
	delay(1);

	while(IIC_Wait_Ack(this));	//阻塞从机应答
}

uint8_t I2C_Recieve_Byte(struct IIC_COMMON *this)
{
	uint8_t rebyte=0,i;

	this->scl->WritePin(this->scl,GPIO_PIN_RESET);	//时钟线置低
	delay(1);
	for(i=0;i<8;i++){
	    this->scl->WritePin(this->scl,GPIO_PIN_SET); //时钟线置高
	    delay(1);
	    rebyte=rebyte<<1;
	    if(this->sda->ReadPin(this->sda)){
	    	rebyte |= 0x01;
	    }
	    this->scl->WritePin(this->scl,GPIO_PIN_RESET);	//时钟线置低
	    delay(2);
	}
	this->sda->WritePin(this->sda,GPIO_PIN_SET);
	delay(1);
	this->scl->WritePin(this->scl,GPIO_PIN_SET);    //给从机应答
	delay(2);
	this->scl->WritePin(this->scl,GPIO_PIN_RESET);    //时钟线置低
	return rebyte;
}

void WriteOneByte(struct IIC_COMMON *this,uint8_t WriteAddr, uint8_t DataToWrite)//向AT24C02指定的地址写入一个字节
{
    I2C_Start(this); //发送起始信号
    I2C_Send_Byte(this,this->addr|0x00);  //设备地址且传输方向位设置为0
    delay(1);
    I2C_Send_Byte(this,WriteAddr);//发送地址
    I2C_Send_Byte(this,DataToWrite);     //发送字节
    I2C_Stop(this);//产生一个停止条件
    delay(100);// 这个延时绝对不能去掉
}
uint8_t ReadOneByte(struct IIC_COMMON *this,uint8_t ReadAddr) //从AT24C02指定的地址读取一个字节
{
    uint8_t temp=0;

    I2C_Start(this);//发送起始信号
    I2C_Send_Byte(this,this->addr);  //设备地址 且传输方向位设置为0
    delay(1);
    I2C_Send_Byte(this,ReadAddr);   //发送地址
    I2C_Start(this);
    I2C_Send_Byte(this,this->addr|0x01);  //设备地址 且传输方向位设置为1
    delay(1);
    temp=I2C_Recieve_Byte(this);   //接受一个字节
    I2C_Stop(this);//产生一个停止条件

    return temp;
}

void ReadBytes(struct IIC_COMMON *this,uint8_t ReadAddr,uint8_t *Buffer,uint16_t Num)//从指定地址连续读取多个字节
{
	while(Num)
	{
	    *Buffer++=ReadOneByte(this,ReadAddr++);
	    Num--;
	}
	Num=5;
}

void WriteBytes(struct IIC_COMMON *this,uint8_t WriteAddr,uint8_t *Buffer,uint16_t Num)//向指定地址连续写入过个字节
{
	while(Num--)
	{
	    WriteOneByte(this,WriteAddr,*Buffer);
	    WriteAddr++;
	    Buffer++;
	}
}

//PB6 SCL    PB7 SDA
IIC_COMMON* new_IIC(struct IIC_COMMON *this ,GPIO_TypeDef  *sda_port,uint32_t sda_pin,GPIO_TypeDef  *scl_port,uint32_t scl_pin,uint8_t s_addr)
{
	this = (struct IIC_COMMON*) calloc(1, sizeof(struct IIC_COMMON));
	this->scl = new_Gpio(this->scl, scl_port, scl_pin, GPIO_MODE_OUTPUT_OD,
	GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH);
	this->sda = new_Gpio(this->sda, sda_port, sda_pin, GPIO_MODE_OUTPUT_OD,
			GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH);
	this->addr = s_addr;
	this->I2C_Start=I2C_Start;
	this->I2C_Stop=I2C_Stop;
	this->I2C_Send_Byte=I2C_Send_Byte;
	this->I2C_Recieve_Byte=I2C_Recieve_Byte;
	this->ReadOneByte=ReadOneByte;
	this->WriteOneByte=WriteOneByte;
	this->ReadBytes=ReadBytes;
	this->WriteBytes=WriteBytes;
	return this;
}
